/**
 * <b>项目名称：</b>网络应用框架<br/>
 * <b>包    名：</b>com.hhwy.framework.util<br/>
 * <b>文 件 名：</b>ClassResourcesUtil.java<br/>
 * <b>版本信息：</b><br/>
 * <b>日    期：</b>2016年9月10日-上午10:36:56<br/>
 * <b>Copyright (c)</b> 2016恒华伟业科技股份有限公司-版权所有<br/>
 * 
 */
package com.hhwy.framework.util;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;

 /**
 * <b>类 名 称：</b>ClassHelper<br/>
 * <b>类 描 述：</b>class帮助类<br/>
 * <b>创 建 人：</b>liuhai<br/>
 * <b>修 改 人：</b>liuhai<br/>
 * <b>修改时间：</b>2016年9月10日 上午10:36:56<br/>
 * <b>修改备注：</b><br/>
 * @version 1.0.0<br/>
 */
public class ClassHelper {
	private static Logger log = LoggerFactory.getLogger(ClassHelper.class);
	/**
	 * 子类搜索所在的classloader
	 */
	private ClassLoader contextClassLoader;
	
	/**
	 * 父接口或父类
	 */
	private Class<?> rootClass;
	
	/**
	 * 子类搜索的范围
	 */
	List<File> files;
	
	/**
	 * 遍历指定文件下，某个指定接口或父类的所有子类
	 *
	 * @param contextClassLoader
	 * @param rootClassName
	 * @param files
	 */
	public ClassHelper(ClassLoader contextClassLoader, String rootClassName, List<File> files){
		this.contextClassLoader = contextClassLoader;
		try {
			this.rootClass = Class.forName(rootClassName, false, contextClassLoader);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.files = files;
	}
	
	/**
	 * 判断类是否子类.<br>
	 * 首先,类不能是抽象的,其次,类必须是子类
	 * 
	 * @param c 类
	 * @return 是否是函数类
	 */
	private boolean isChildren(Class<?> c) {
		if (c == null) {
			return false;
		}
		if (c.isInterface()) {
			return false;
		}
		if (Modifier.isAbstract(c.getModifiers())) {
			return false;// 抽象
		}
		// Class<?>[] interfaces = c.getInterfaces();
		// if (interfaces == null || interfaces.length == 0) {
		// return false;
		// }
		// for (Class<?> i : interfaces) {
		// if (i == IFunction.class) {
		// return true;
		// }
		// }
		return rootClass.isAssignableFrom(c);
	}
	
	/**
	 * 获取包下所有的函数实现类
	 * 
	 * @param pkg 包名
	 * @return 类列表
	 */
	public List<Class<?>> getClasses(String pkg) {
		List<Class<?>> list = new ArrayList<Class<?>>();
		for (File f : this.files) {
			// 如果是以文件的形式保存在服务器上
			if (f.isDirectory()) {
				// 获取包的物理路径
				String path = pkg.replace('.', File.separatorChar);
				dirWalker(path, f, list);
			} else {//尝试是否是jar文件
				// 获取jar
				JarFile jar = null;
				try {
					jar = new JarFile(f);
				} catch (IOException e) {
					// 有可能不是一个jar
				}
				if (jar == null) {
					continue;
				}
				String path = pkg.replace('.', '/');
				// 从此jar包 得到一个枚举类
				Enumeration<JarEntry> entries = jar.entries();
				// 同样的进行循环迭代
				while (entries.hasMoreElements()) {
					// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
					JarEntry entry = entries.nextElement();
					String name = entry.getName();
					// 如果是以/开头的
					if (name.charAt(0) == '/') {
						// 获取后面的字符串
						name = name.substring(1);
					}
					// 如果前半部分和定义的包名相同
					if (name.contains(path)) {
						if (name.endsWith(".class") && !entry.isDirectory()) {
							name = name.replace("/", ".").substring(0,
									name.lastIndexOf("."));
							try {
								Class<?> c = Class.forName(name, false, contextClassLoader);
								if (isChildren(c)) {
									list.add(c);
								}
							} catch (Exception e) {
								// 找不到无所谓了
							}
						}
					}
				}
				try {
					jar.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return list;
	}
	
	/**
	 * 遍历文件夹下所有的类
	 * 
	 * @param path 包路径
	 * @param file 文件
	 * @param list 保存类列表
	 */
	private void dirWalker(String path, File file, List<Class<?>> list) {
		if (file.exists()) {
			if (file.isDirectory()) {
				for (File f : file.listFiles()) {
					dirWalker(path, f, list);
				}
			} else {
				Class<?> c = loadClassByFile(path, file);
				if (c != null) {
					list.add(c);
				}
			}
		}
	}

	/**
	 * 从文件加载类
	 * 
	 * @param pkg 包路径
	 * @param file 文件
	 * @return 类或者null
	 */
	private Class<?> loadClassByFile(String pkg, File file) {
		if (!file.isFile()) {
			return null;
		}
		String name = file.getName();
		if (name.endsWith(".class")) {
			String ap = file.getAbsolutePath();
			if (!ap.contains(pkg)) {
				return null;
			}
			name = ap.substring(ap.indexOf(pkg) + pkg.length());
			if (name.startsWith(File.separator)) {
				name = name.substring(1);
			}
			String path = (pkg + "." + name.substring(0, name.lastIndexOf(".")))
					.replace(File.separatorChar, '.');
			try {
				Class<?> c = Class.forName(path, false, contextClassLoader);
				if (isChildren(c)) {
					return c;
				}
			} catch (ClassNotFoundException e) {
				// do nothing
			}
		}
		return null;
	}

	/**
	 * getMainServletClassFiles(获取mainServlet背景下的class资源，包括jar包与classes文件夹)<br/>
	 * @return 
	 * List<File>
	 * @exception 
	 * @since  1.0.0
	 */
	public static List<File> getMainServletClassResources() {
		//获取mainservlet下的编译文件
		List<File> files = new ArrayList<File>();
		//获取jar包
		File jarsDir = new File(IoTools.getABSPATH() + "/WEB-INF/lib");
		String[] jars = jarsDir.list(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				if (name.endsWith(".jar"))
					return true;
				return false;
			}
		});
		
		for(String jar : jars){
			files.add(new File(jarsDir + "/" + jar));
		}
		
		files.add(new File(IoTools.getClassesPath()));
		return files;
	}
	

	/**
	 * getPluginClassResources(获取插件的class资源，包括jar包与classes文件夹)<br/>
	 * @param libPath
	 * @return 
	 * List<File>
	 * @exception 
	 * @since  1.0.0
	 */
	public static List<File> getPluginClassResources (String libPath) {
		List<File> classResources = new ArrayList<>();
		
		//添加插件中的classes文件
		String classesPath = "";
		if(libPath.contains("/WEB-INF/lib")){
			classesPath = libPath.substring(0, libPath.indexOf("/WEB-INF/lib")) +  "/WEB-INF/classes";
		}else{
			classesPath = libPath + "/WEB-INF/classes";
		}
		if(new File(classesPath).exists()){
			classResources.add(new File(classesPath));
		}
		
		//添加插件中的lib包中的jar文件
		File dir = new File(libPath);
		String[] files = dir.list(new FilenameFilter() {

			@Override
			public boolean accept(File dir, String name) {
				if (name.endsWith(".jar"))
					return true;

				return false;
			}
		});
		if(files != null){
			for (int j = 0; j < files.length; j++) {
				files[j] = libPath + "/" + files[j];
				classResources.add(new File(files[j]));
			}
		}
		
		return classResources;
	}
	
	
	/**************************************************************************************/
	/**
	 * getTargetObject(获取某个(代理)对象的真实对象)<br/>
	 * @param proxy
	 * @return 
	 * Object
	 * @exception 
	 * @since  1.0.0
	 */
	public static Object getTargetObject(Object proxy) {

		if (!AopUtils.isAopProxy(proxy)) { // 判断是否是代理类
			return proxy;
		}

		try {
			return getTargetObject(getProxyTargetObject(proxy));
		} catch (Exception e) {
			log.error("获取代理对象的真实对象失败【"+proxy.getClass()+"】");
			return proxy;
		}
	}

	private static Object getProxyTargetObject(Object proxy) throws Exception {
		Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
		h.setAccessible(true);
		AopProxy aopProxy = (AopProxy) h.get(proxy);
		Field advised = aopProxy.getClass().getDeclaredField("advised");
		advised.setAccessible(true);
		return ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
	}
	
	/**
	 * getAllClassNameOfJar(获取jar包中的所有class文件名称，不带.class后缀)<br/>
	 * @param jarFilePath
	 * @return 
	 * List<String>
	 * @exception 
	 * @since  1.0.0
	 */
	public static List<String> getAllClassNameOfJar(String jarFilePath){
		List<String> list = new ArrayList<String>();
		JarFile jar = null;
		try {
			jar = new JarFile(jarFilePath);
		} catch (IOException e) {
			// 有可能不是一个jar
			return list;
		}
		// 从此jar包 得到一个枚举类
		Enumeration<JarEntry> entries = jar.entries();
		// 同样的进行循环迭代
		while (entries.hasMoreElements()) {
			// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
			JarEntry entry = entries.nextElement();
			String name = entry.getName();
			// 如果是以/开头的
			if (name.charAt(0) == '/') {
				// 获取后面的字符串
				name = name.substring(1);
			}
			if (name.endsWith(".class") && !entry.isDirectory()) {
				name = name.replace("/", ".").substring(0,
						name.lastIndexOf("."));
				list.add(name);
			}
		}
		try {
			if(jar != null)
				jar.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return list;
	}
}
